//***************************************************************************
//   Copyright(c)2020, Xidian University D405.
//   All rights reserved
//
//   IP Name         :   Null
//   File name       :   bus_to_cr.v
//   Module name     :   bus_to_cr
//   Author          :   Wang Zekun
//   Date            :   2022/07/10
//   Version         :   v1.0
//   Verison History :   v1.0/
//   Edited by       :   Wang Zekun
//   Modification history : v1.0 Initial revision
//
// ----------------------------------------------------------------------------
// Version 1.0       Date(2022/07/10)
// Abstract : bridge from the unstandard bus protocal inside hdfwd to 
// 		phy cr protocol
//
//-----------------------------------------------------------------------------
// Programmer's model
//
//-----------------------------------------------------------------------------
//interface list :
//
// 4 line define has moved to top_define.v                
//`define ADDR_PHY_ACCESS_CFG                             89         // RW // 32bit    //PHY
//`define PHY_HEAD_BASE_ADDR                              7'h44      //PHY_HEAD
//`define PHY_ROM_BASE_ADDR                               7'h54      //PHY_ROM
//`define PHY_TAIL_BASE_ADDR                              7'h58      //PHY_TAIL

`include "top_define.v"
module bus_to_cr #(
  parameter CR_ADDR_WIDTH = 16
)(
  // bus
  input  wire                           hclk_i,
  input  wire                           hresetn_i,
  input  wire                           pkt_clk_i,
  input  wire                           pkt_rstn_i,
  input  wire [16:0]                    np_addr_i,
  input  wire                           np_wr_i,
  input  wire [31:0]                    np_wr_data_i,
  input  wire                           np_rd_i,
  output wire [31:0]                    np_rd_data_o,
  output wire                           rd_data_vld_o,
 
  input  wire				testmode, 
  output wire                           cr_para_clk_o,
  output wire [CR_ADDR_WIDTH-1:0]       cr_para_addr_o,
  output wire                           cr_para_sel_o,
  output wire                           cr_para_wr_en_o,
  output wire                           cr_para_rd_en_o,
  output wire [15:0]                    cr_para_wr_data_o,

  input  wire [15:0]                    cr_para_rd_data_i,
  input  wire                           cr_para_ack_i
);
  reg  [CR_ADDR_WIDTH-1:0]      buf_addr;
  reg  [31:0]                   buf_wdata;
  reg  [31:0]                   buf_rdata;

  wire				write_reg_valid;
  wire				read_reg_valid;
  wire                          addr_range_en;
  wire                          addr_phy_rom_en;
  wire                          addr_phy_ram_en;

  wire                          phy_access_en;
  wire                          phy_addr_usage;
  reg  [1:0]                    phy_access_cfg;
  reg  [16:0]                   address_turn;
  
  wire                          cr_para_wr_en;
  wire                          cr_para_rd_en;
  wire                          cr_rd_ack;
  reg                           rd_vld;
  reg                           phy_rd_process;
  
  wire				sync_phy_resetn; //low active
  hdfwd_rstn_sync i_sync_phy_resetn ( sync_phy_resetn, cr_para_clk_o, hresetn_i, hresetn_i, testmode, 1'b1 );

  assign write_reg_valid = (np_addr_i[16:10] == 7'h00) & np_wr_i & (np_addr_i[6:0] == `ADDR_PHY_ACCESS_CFG);
  assign read_reg_valid  = (np_addr_i[16:10] == 7'h00) & np_rd_i & (np_addr_i[6:0] == `ADDR_PHY_ACCESS_CFG);
  assign addr_range_en   = (np_addr_i[16:10] < `PHY_TAIL_BASE_ADDR) & (np_addr_i[16:10] >= `PHY_HEAD_BASE_ADDR);
  assign addr_phy_rom_en = (np_addr_i[16:10] >= `PHY_ROM_BASE_ADDR);
  assign addr_phy_ram_en = phy_addr_usage & addr_phy_rom_en;
  
  assign cr_para_sel_o     = phy_access_en;
  assign cr_para_wr_en     = np_wr_i & addr_range_en & phy_access_en;
  assign cr_para_rd_en     = np_rd_i & addr_range_en & phy_access_en;
  assign cr_para_addr_o    = buf_addr;
  assign cr_para_wr_data_o = buf_wdata[15:0];
  assign np_rd_data_o      = {16'h0000,buf_rdata};
  assign rd_data_vld_o     = rd_vld;

  always @(*) begin
    address_turn = addr_range_en ? {np_addr_i[16:10] - 7'h44,np_addr_i[9:0]} : 17'h0_0000;
  end

  always @(posedge pkt_clk_i or negedge pkt_rstn_i) begin
    if(~pkt_rstn_i) begin
      phy_access_cfg  <= 2'b0;
    end
    else if(write_reg_valid) begin
      phy_access_cfg  <= np_wr_data_i;
    end
    else begin
      phy_access_cfg  <= phy_access_cfg;
    end
  end
  assign phy_access_en  = phy_access_cfg[0];
  assign phy_addr_usage = phy_access_cfg[1];

  always @(posedge pkt_clk_i or negedge pkt_rstn_i) begin
    if(~pkt_rstn_i) begin
      buf_addr  <= {CR_ADDR_WIDTH{1'b0}};
    end
    else if(phy_access_en & (np_wr_i | np_rd_i)) begin
      if(~phy_addr_usage & addr_phy_rom_en)
        buf_addr  <= {{CR_ADDR_WIDTH-15{1'b0}},3'b100,address_turn[11:0]};
      else if(phy_addr_usage & addr_phy_ram_en)
        buf_addr  <= {{CR_ADDR_WIDTH-15{1'b0}},3'b110,address_turn[11:0]};
      else
        buf_addr  <= address_turn;
    end
    else begin
      buf_addr  <= buf_addr;
    end
  end

  always @(posedge pkt_clk_i or negedge pkt_rstn_i) begin
    if(~pkt_rstn_i) begin
      buf_wdata <= 32'b0;
    end
    else if(phy_access_en & np_wr_i) begin
      buf_wdata <= np_wr_data_i;
    end
    else begin
      buf_wdata <= buf_wdata;
    end
  end


  always @(posedge pkt_clk_i or negedge pkt_rstn_i) begin
    if(~pkt_rstn_i) begin
      rd_vld <= 1'b0;
    end
    else if(~phy_access_en & np_rd_i & addr_range_en) begin
      rd_vld <= 1'b1;
    end
    else if(read_reg_valid) begin
      rd_vld <= 1'b1;
    end
    else if(phy_rd_process & cr_rd_ack) begin
      rd_vld <= cr_rd_ack;
    end
    else begin
      rd_vld <= 1'b0;
    end
  end

  always @(posedge pkt_clk_i or negedge pkt_rstn_i) begin
    if(~pkt_rstn_i) begin
      phy_rd_process <= 1'b0;
    end
    else if(rd_vld) begin
      phy_rd_process <= 1'b0;
    end
    else if(cr_para_rd_en) begin
      phy_rd_process <= 1'b1;
    end
    else begin
      phy_rd_process <= phy_rd_process;
    end
  end

  always @(*) begin
    if (np_addr_i[16:10] == 7'h00)
      buf_rdata <= (np_addr_i[6:0] == `ADDR_PHY_ACCESS_CFG) ? {14'h0000,phy_access_cfg} : 16'h0000;
    else
      buf_rdata <= cr_para_rd_data_i;
  end

  cr_clk_div u_cr_clk_div(
    .clk_i              (hclk_i),
    .resetn_i           (hresetn_i),
    .clk_o              (cr_para_clk_o)
  );
  
  cr_fast_to_slow u_cr_fast_to_slow_wr(
    .clk_f_i            (pkt_clk_i),
    .resetn_f_i         (pkt_rstn_i),
    .signal_f_i         (cr_para_wr_en),
    .clk_s_i            (cr_para_clk_o),
    .resetn_s_i         (sync_phy_resetn),
    .signal_s_o         (cr_para_wr_en_o)
  );

  cr_fast_to_slow u_cr_fast_to_slow_rd(
    .clk_f_i            (pkt_clk_i),
    .resetn_f_i         (pkt_rstn_i),
    .signal_f_i         (cr_para_rd_en),
    .clk_s_i            (cr_para_clk_o),
    .resetn_s_i         (sync_phy_resetn),
    .signal_s_o         (cr_para_rd_en_o)
  );

  cr_slow_to_fast u_cr_slow_to_fast_ack(
    .clk_f_i            (pkt_clk_i),
    .resetn_f_i         (pkt_rstn_i),
    .signal_s_i         (cr_para_ack_i),
    .signal_f_o         (cr_rd_ack)
  );
endmodule
